package top.wilsonlv.jaguar.dept.data.permission;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import lombok.SneakyThrows;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import org.springframework.stereotype.Component;
import top.wilsonlv.jaguar.basecrud.util.LongUtil;
import top.wilsonlv.jaguar.security.model.SecurityUser;
import top.wilsonlv.jaguar.security.util.SecurityUtil;

import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author lvws
 * @since 2022/8/9
 */
@Component
public class DeptDataPermissionHandler implements DataPermissionHandler {

    private static final ThreadLocal<DeptDataPermission<?>> DEPT_DATA_PERMISSION = new ThreadLocal<>();

    public static <T> void start(SFunction<T, ?> column) {
        DEPT_DATA_PERMISSION.set(new DeptDataPermission<>(column));
    }

    public static <T> void start(SFunction<T, ?> column, String alias) {
        DEPT_DATA_PERMISSION.set(new DeptDataPermission<>(column, alias));
    }

    public static void end() {
        DEPT_DATA_PERMISSION.remove();
    }

    @SneakyThrows
    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        SecurityUser currentUser = SecurityUtil.getCurrentUser(false);
        if (currentUser == null || LongUtil.isNull(currentUser.getDeptId()) || DEPT_DATA_PERMISSION.get() == null) {
            return where;
        }

        DeptDataPermission<?> deptDataPermission = DEPT_DATA_PERMISSION.get();
        SerializedLambda serializedLambda = SerializedLambda.extract(deptDataPermission.getColumn());

        Field implClassField = serializedLambda.getClass().getDeclaredField("implClass");
        implClassField.setAccessible(true);
        String implClass = ((String) implClassField.get(serializedLambda)).replace("/", ".");

        String fieldName = StringUtils.firstToLowerCase(serializedLambda.getImplMethodName().substring("get".length()));
        Field field = Class.forName(implClass).getDeclaredField(fieldName);

        String columnName;
        TableField tableField = field.getAnnotation(TableField.class);
        if (tableField != null && StringUtils.isNotBlank(tableField.value())) {
            columnName = tableField.value();
        } else {
            columnName = StringUtils.camelToUnderline(fieldName);
        }

        Column column = new Column(StringUtils.isNotBlank(deptDataPermission.getAlias()) ? deptDataPermission.getAlias() + '.' + columnName : columnName);
        List<Expression> expressions = currentUser.getGrantedDeptIds()
                .stream().map(LongValue::new).collect(Collectors.toList());

        Expression expression;
        if (expressions.size() == 1) {
            expression = new EqualsTo(column, expressions.get(0));
        } else {
            InExpression inExpression = new InExpression();
            inExpression.setLeftExpression(column);
            inExpression.setRightItemsList(new ExpressionList(expressions));
            expression = inExpression;
        }
        return new AndExpression(where, expression);
    }
}
